#include <stdio.h>
#include <hardware/spi.h>
#include "spi_flash.h"
#include "pico/stdlib.h"

#define SPI0_TX  19
#define SPI0_SCK 18
#define SPI0_CSN 17
#define SPI0_RX  16

//#define SPI_FLASH_FREQ (62500*1000) // 125MHz/2 = 62.5MHz
#define SPI_FLASH_FREQ (31250*1000) // 125MHz/4 = 31.25MHz

#define FLASH_DEVICE_RESET                              0xFF
#define FLASH_JEDEC_ID                                  0x9F
#define FLASH_READ_STATUS_REGISTER                      0x0F //或0x05
#define FLASH_WRITE_STATUS_REGISTER                     0x1F //或0x01
#define FLASH_STATUS_REG1_ADDR                          0xA0 //低四位可以为任意值
#define FLASH_STATUS_REG2_ADDR                          0xB0 //低四位可以为任意值
#define FLASH_STATUS_REG3_ADDR                          0xC0 //低四位可以为任意值
#define FLASH_WRITE_ENABLE                              0x06
#define FLASH_WRITE_DISABLE                             0x04
#define FLASH_BAD_BLOCK_MANAGEMENT                      0xA1
#define FLASH_READ_BBM_LUT                              0xA5
#define FLASH_LAST_ECC_FAILURE_PAGE_ADDR                0xA9
#define FLASH_BLOCK_ERASE                               0xD8
#define FLASH_PROGRAM_DATA_LOAD                         0x02
#define FLASH_RANDOM_PROGRAM_DATA_LOAD                  0x84
#define FLASH_QUAD_PROGRAM_DATA_LOAD                    0x32
#define FLASH_RANDOM_QUADPROGRAM_DATA_LOAD              0x34
#define FLASH_PROGRAM_ECECUTE                           0x10
#define FLASH_PAGE_DATA_READ                            0x13
#define FLASH_READ                                      0x03
#define FLASH_FAST_READ                                 0x0B
#define FLASH_FAST_READ_WITH_4BYTE_ADDR                 0x0C
#define FLASH_FAST_READ_DUAL_OUTPUT                     0x3B
#define FLASH_FAST_READ_DUAL_OUTPUT_WITH_4BYTE_ADDR     0x3C
#define FLASH_FAST_READ_QUAD_OUTPUT                     0x6B
#define FLASH_FAST_READ_QUAD_OUTPUT_WITH_4BYTE_ADDR     0x6C
#define FLASH_FAST_READ_DUAL_IO                         0xBB
#define FLASH_FAST_READ_DUAL_IO_WITH_4BYTE_ADDR         0xBC
#define FLASH_FAST_READ_QUAD_IO                         0xEB
#define FLASH_FAST_READ_QUAD_IO_WITH_4BYTE_ADDR         0xEC

// SR3状态寄存器标识
#define FLASH_SR_BUSY                                   0x01//芯片忙，正在进行写入或擦除等操作
#define FLASH_SR_WEL                                    0x02//可写标识位
#define FLASH_SR_ERASE_FAIL                             0x04//擦除失败
#define FLASH_SR_WRITE_FAIL                             0x08//编程（写入）失败
#define FLASH_SR_ECC_LESS_THAN_4BIT_ERR                 0x10//ECC校验有1-4bit的错误，已被纠正
#define FLASH_SR_ECC_MORE_THAN_4BIT_ERR                 0x20//ECC校验有多于4bit的错误，无法被纠正
#define FLASH_SR_ECC_DATA_DAMAGED                       0x30//每页都有多于4bit的错误，不建议继续使用
#define FLASH_SR_BBM_LUT_FULL                           0x40//坏块管理表已满

static const uint32_t crc_table[256] = {
    0x00000000UL,0x77073096UL,0xEE0E612CUL,0x990951BAUL,0x076DC419UL,0x706AF48FUL,0xE963A535UL,0x9E6495A3UL,
    0x0EDB8832UL,0x79DCB8A4UL,0xE0D5E91EUL,0x97D2D988UL,0x09B64C2BUL,0x7EB17CBDUL,0xE7B82D07UL,0x90BF1D91UL,
    0x1DB71064UL,0x6AB020F2UL,0xF3B97148UL,0x84BE41DEUL,0x1ADAD47DUL,0x6DDDE4EBUL,0xF4D4B551UL,0x83D385C7UL,
    0x136C9856UL,0x646BA8C0UL,0xFD62F97AUL,0x8A65C9ECUL,0x14015C4FUL,0x63066CD9UL,0xFA0F3D63UL,0x8D080DF5UL,
    0x3B6E20C8UL,0x4C69105EUL,0xD56041E4UL,0xA2677172UL,0x3C03E4D1UL,0x4B04D447UL,0xD20D85FDUL,0xA50AB56BUL,
    0x35B5A8FAUL,0x42B2986CUL,0xDBBBC9D6UL,0xACBCF940UL,0x32D86CE3UL,0x45DF5C75UL,0xDCD60DCFUL,0xABD13D59UL,
    0x26D930ACUL,0x51DE003AUL,0xC8D75180UL,0xBFD06116UL,0x21B4F4B5UL,0x56B3C423UL,0xCFBA9599UL,0xB8BDA50FUL,
    0x2802B89EUL,0x5F058808UL,0xC60CD9B2UL,0xB10BE924UL,0x2F6F7C87UL,0x58684C11UL,0xC1611DABUL,0xB6662D3DUL,
    0x76DC4190UL,0x01DB7106UL,0x98D220BCUL,0xEFD5102AUL,0x71B18589UL,0x06B6B51FUL,0x9FBFE4A5UL,0xE8B8D433UL,
    0x7807C9A2UL,0x0F00F934UL,0x9609A88EUL,0xE10E9818UL,0x7F6A0DBBUL,0x086D3D2DUL,0x91646C97UL,0xE6635C01UL,
    0x6B6B51F4UL,0x1C6C6162UL,0x856530D8UL,0xF262004EUL,0x6C0695EDUL,0x1B01A57BUL,0x8208F4C1UL,0xF50FC457UL,
    0x65B0D9C6UL,0x12B7E950UL,0x8BBEB8EAUL,0xFCB9887CUL,0x62DD1DDFUL,0x15DA2D49UL,0x8CD37CF3UL,0xFBD44C65UL,
    0x4DB26158UL,0x3AB551CEUL,0xA3BC0074UL,0xD4BB30E2UL,0x4ADFA541UL,0x3DD895D7UL,0xA4D1C46DUL,0xD3D6F4FBUL,
    0x4369E96AUL,0x346ED9FCUL,0xAD678846UL,0xDA60B8D0UL,0x44042D73UL,0x33031DE5UL,0xAA0A4C5FUL,0xDD0D7CC9UL,
    0x5005713CUL,0x270241AAUL,0xBE0B1010UL,0xC90C2086UL,0x5768B525UL,0x206F85B3UL,0xB966D409UL,0xCE61E49FUL,
    0x5EDEF90EUL,0x29D9C998UL,0xB0D09822UL,0xC7D7A8B4UL,0x59B33D17UL,0x2EB40D81UL,0xB7BD5C3BUL,0xC0BA6CADUL,
    0xEDB88320UL,0x9ABFB3B6UL,0x03B6E20CUL,0x74B1D29AUL,0xEAD54739UL,0x9DD277AFUL,0x04DB2615UL,0x73DC1683UL,
    0xE3630B12UL,0x94643B84UL,0x0D6D6A3EUL,0x7A6A5AA8UL,0xE40ECF0BUL,0x9309FF9DUL,0x0A00AE27UL,0x7D079EB1UL,
    0xF00F9344UL,0x8708A3D2UL,0x1E01F268UL,0x6906C2FEUL,0xF762575DUL,0x806567CBUL,0x196C3671UL,0x6E6B06E7UL,
    0xFED41B76UL,0x89D32BE0UL,0x10DA7A5AUL,0x67DD4ACCUL,0xF9B9DF6FUL,0x8EBEEFF9UL,0x17B7BE43UL,0x60B08ED5UL,
    0xD6D6A3E8UL,0xA1D1937EUL,0x38D8C2C4UL,0x4FDFF252UL,0xD1BB67F1UL,0xA6BC5767UL,0x3FB506DDUL,0x48B2364BUL,
    0xD80D2BDAUL,0xAF0A1B4CUL,0x36034AF6UL,0x41047A60UL,0xDF60EFC3UL,0xA867DF55UL,0x316E8EEFUL,0x4669BE79UL,
    0xCB61B38CUL,0xBC66831AUL,0x256FD2A0UL,0x5268E236UL,0xCC0C7795UL,0xBB0B4703UL,0x220216B9UL,0x5505262FUL,
    0xC5BA3BBEUL,0xB2BD0B28UL,0x2BB45A92UL,0x5CB36A04UL,0xC2D7FFA7UL,0xB5D0CF31UL,0x2CD99E8BUL,0x5BDEAE1DUL,
    0x9B64C2B0UL,0xEC63F226UL,0x756AA39CUL,0x026D930AUL,0x9C0906A9UL,0xEB0E363FUL,0x72076785UL,0x05005713UL,
    0x95BF4A82UL,0xE2B87A14UL,0x7BB12BAEUL,0x0CB61B38UL,0x92D28E9BUL,0xE5D5BE0DUL,0x7CDCEFB7UL,0x0BDBDF21UL,
    0x86D3D2D4UL,0xF1D4E242UL,0x68DDB3F8UL,0x1FDA836EUL,0x81BE16CDUL,0xF6B9265BUL,0x6FB077E1UL,0x18B74777UL,
    0x88085AE6UL,0xFF0F6A70UL,0x66063BCAUL,0x11010B5CUL,0x8F659EFFUL,0xF862AE69UL,0x616BFFD3UL,0x166CCF45UL,
    0xA00AE278UL,0xD70DD2EEUL,0x4E048354UL,0x3903B3C2UL,0xA7672661UL,0xD06016F7UL,0x4969474DUL,0x3E6E77DBUL,
    0xAED16A4AUL,0xD9D65ADCUL,0x40DF0B66UL,0x37D83BF0UL,0xA9BCAE53UL,0xDEBB9EC5UL,0x47B2CF7FUL,0x30B5FFE9UL,
    0xBDBDF21CUL,0xCABAC28AUL,0x53B39330UL,0x24B4A3A6UL,0xBAD03605UL,0xCDD70693UL,0x54DE5729UL,0x23D967BFUL,
    0xB3667A2EUL,0xC4614AB8UL,0x5D681B02UL,0x2A6F2B94UL,0xB40BBE37UL,0xC30C8EA1UL,0x5A05DF1BUL,0x2D02EF8DUL,
};


static inline void cs_select(void) {
    asm volatile("nop \n nop \n nop");
    gpio_put(SPI0_CSN, 0);
    asm volatile("nop \n nop \n nop");
}

static inline void cs_deselect(void) {
    asm volatile("nop \n nop \n nop");
    gpio_put(SPI0_CSN, 1);
    asm volatile("nop \n nop \n nop");
}

// 验证器件的ID是否正确
static bool flash_read_device_id(void) 
{
    cs_select();
    //  Read JEDEC ID (9Fh) 
    uint8_t cmdbuf[2] = {FLASH_JEDEC_ID, 0};
    uint8_t buf[3];
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    spi_read_blocking(spi0, 0, buf, 3);
    cs_deselect();
    // ID should 0xef 0xaa 0x21
    return buf[0] == 0xef && buf[1] == 0xaa && buf[2] == 0x21;
}


// 等待busy变为0,并且返回sr3
static uint8_t flash_busy_wait(void)
{
    uint8_t sr3[1];
    uint8_t cmdbuf[2] = {FLASH_READ_STATUS_REGISTER, FLASH_STATUS_REG3_ADDR};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    do{
        spi_read_blocking(spi0, 0, sr3, 1);
    }while(sr3[0] & FLASH_SR_BUSY);
    cs_deselect();
    return sr3[0];
}
// block_address 0 - 1023
// 这次从X宝买的FLASH似乎是翻新的，0x000地址有数据，0x800全都是0xff，无法判断坏快。
// 实测前70MB无坏快，暂不编写坏快处理程序
/*
uint8_t flash_bad_block_scan(uint16_t block_address)
{
    block_address <<= 6;
    uint8_t cmdbuf_1[4] = {FLASH_PAGE_DATA_READ, 0, block_address>>8, block_address&0xff};
    cs_select();
    spi_write_blocking(spi0, cmdbuf_1, sizeof(cmdbuf_1));
    cs_deselect();
    flash_busy_wait(); // about 60us with ECC, 25us without ECC 

    uint8_t cmdbuf_2[4] = {FLASH_READ, 0x08, 0x00, 0};
    uint8_t block_first_byte;
    cs_select();
    spi_write_blocking(spi0, cmdbuf_2, sizeof(cmdbuf_2));
    spi_read_blocking(spi0, 0, &block_first_byte, 1);
    cs_deselect();
    //printf("%x\n",block_first_byte);
    return block_first_byte != 0xff;
}
void flash_dump_bbm(void) 
{
    cs_select();
    uint8_t cmdbuf[2] = {FLASH_READ_BBM_LUT, 0};
    uint8_t buf[80];
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    spi_read_blocking(spi0, 0, buf, 80);
    cs_deselect();
    for(int i=0;i<80;i+=4){
        printf("%02x%02x -> %02x%02x\n",buf[i],buf[i+1],buf[i+2],buf[i+3]);
    }
}
*/
// page_address 0 - 65535

static void flash_write_sr(uint8_t addr, uint8_t date)
{
    uint8_t cmdbuf[3] = {FLASH_WRITE_STATUS_REGISTER, addr, date};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    cs_deselect();
}

static uint8_t flash_read_sr(uint8_t addr)
{
    uint8_t tmp[1];
    uint8_t cmdbuf[2] = {FLASH_READ_STATUS_REGISTER, addr};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    spi_read_blocking(spi0, 0, tmp, 1);
    cs_deselect();
    return tmp[0];
}

void flash_init(void)
{
    // Enable SPI 0 at SPI_FLASH_FREQ and connect to GPIOs
    spi_init(spi0, SPI_FLASH_FREQ);
    //printf("spi_frep=%d\n",spi_set_baudrate(spi0, SPI_FLASH_FREQ));
    gpio_set_function(SPI0_RX, GPIO_FUNC_SPI);
    gpio_set_function(SPI0_SCK, GPIO_FUNC_SPI);
    gpio_set_function(SPI0_TX, GPIO_FUNC_SPI);
    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(SPI0_CSN);
    gpio_put(SPI0_CSN, 1);
    gpio_set_dir(SPI0_CSN, GPIO_OUT);
    // 设置驱动能力
    gpio_set_drive_strength(SPI0_SCK, GPIO_DRIVE_STRENGTH_12MA);
    gpio_set_slew_rate     (SPI0_SCK, GPIO_SLEW_RATE_FAST);
    gpio_set_drive_strength(SPI0_TX,  GPIO_DRIVE_STRENGTH_12MA);
    gpio_set_slew_rate     (SPI0_TX,  GPIO_SLEW_RATE_FAST);
    gpio_set_drive_strength(SPI0_CSN, GPIO_DRIVE_STRENGTH_12MA);
    gpio_set_slew_rate     (SPI0_CSN, GPIO_SLEW_RATE_FAST);
    // 检查ID
    if(!flash_read_device_id()){
        printf("SPI FLASH ID ERROR.\n");
        while(1);
    }
    // 禁用ECC，切换buffer模式
    flash_write_sr(FLASH_STATUS_REG2_ADDR, 0x08);
    // 解除写保护
    flash_write_sr(FLASH_STATUS_REG1_ADDR, 0x00);
}

static void flash_write_enable(void)
{
    uint8_t cmdbuf[1] = {FLASH_WRITE_ENABLE};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    cs_deselect();
}

// 读取2KB
void flash_page_read(uint16_t page_addr, uint8_t *buf)
{
    uint8_t cmdbuf_1[4] = {FLASH_PAGE_DATA_READ, 0, page_addr>>8, page_addr&0xff};
    cs_select();
    spi_write_blocking(spi0, cmdbuf_1, sizeof(cmdbuf_1));
    cs_deselect();
    flash_busy_wait(); // <60us with ECC, <25us without ECC 
    // Read 2048 Byte
    uint8_t cmdbuf_2[4] = {FLASH_READ, page_addr>>8, page_addr&0xff, 0};
    cs_select();    
    spi_write_blocking(spi0, cmdbuf_2, sizeof(cmdbuf_2));
    spi_read_blocking(spi0, 0, buf, 2048);
    cs_deselect();
}

// 写入2KB,成功返回0,失败返回非零
// in_buf长度至少有2112
int flash_write_page(uint16_t page_addr, const uint8_t *in_buf)
{
	// 第1步，使能FLASH写入，写入完成后会自动禁用
    flash_write_enable();
    // 第2步，写缓冲区 02h
    uint8_t cmdbuf[3] = {FLASH_PROGRAM_DATA_LOAD, 0, 0};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    spi_write_blocking(spi0, in_buf, 2112);
    cs_deselect();
    // 第3步，写入FLASH 10h
    uint8_t cmdbuf2[4] = {FLASH_PROGRAM_ECECUTE, 0, page_addr>>8, page_addr&0xff};
    cs_select();
    spi_write_blocking(spi0, cmdbuf2, sizeof(cmdbuf2));
    cs_deselect();
    int flag = flash_busy_wait();
    return flag & FLASH_SR_WRITE_FAIL;
}

// 擦除128KB,成功返回0,失败返回非零
int flash_erase_block(uint16_t page_addr)
{
    flash_write_enable();
    // D8h
    uint8_t cmdbuf[4] = {FLASH_BLOCK_ERASE, 0, page_addr>>8, page_addr&0xff};
    cs_select();
    spi_write_blocking(spi0, cmdbuf, sizeof(cmdbuf));
    cs_deselect();
    int flag = flash_busy_wait();
    return flag & FLASH_SR_ERASE_FAIL;
}


#define LOOKUP_TABLE_SIZE    74091948
uint32_t flash_crc_all_table(void)
{
    uint32_t crc = 0xFFFFFFFFUL;
    int prog_len = LOOKUP_TABLE_SIZE;
	for(unsigned int i=0; i<LOOKUP_TABLE_SIZE/2048+1; i++)
    {
		int max = 2048;
		if(prog_len < max){
			max = prog_len;
		}
		prog_len -= max;
        //
        uint8_t cmdbuf_1[4] = {FLASH_PAGE_DATA_READ, 0, i >> 8, i & 0xff};
        cs_select();
        spi_write_blocking(spi0, cmdbuf_1, sizeof(cmdbuf_1));
        cs_deselect();
        // wait <60us with ECC, <25us without ECC 
        flash_busy_wait(); 
        // read data 03h
        uint8_t tmp[2048];
        uint8_t cmdbuf_2[4] = {FLASH_READ, 0x00, 0x00, 0};
        cs_select();
        spi_write_blocking(spi0, cmdbuf_2, sizeof(cmdbuf_2));
        spi_read_blocking(spi0, 0, tmp, max);
        cs_deselect();

        for(int i = 0; i < max; i ++){
            unsigned char index = (unsigned char)(crc^tmp[i]);
            crc = (crc>>8)^crc_table[index];
        }

        if(i%1024==0){
            printf("\n%d%%", i*100/(LOOKUP_TABLE_SIZE/2048+1));
        }
        if(i%64==0){
            printf(".");
            fflush(stdout);
        }
        
    }
    crc ^= 0xFFFFFFFFUL;
    printf("\nCRC32 = %08X\n", crc);
    return crc;
}

//使用binary crc.py产生
static const uint32_t crc32_checksums[] = {
  0xA6C247E2,  0x1E27A094,  0x98C213BB,  0x462C23CD,  0xD22A3A5,  0x2A56F1F6,  0x5A5052B4,  0xD85488F4,
  0x370CFB25,  0x2B66B6DB,  0xF524C9F8,  0x53AD02FD,  0xC519C1BF,  0xF9E5D520,  0x7B658C79,  0x9D001022,
  0xD6B2605E,  0x98446BEF,  0xD0CFC1BE,  0xFDF91A23,  0x144E96B4,  0x2D4CA78E,  0xF4896424,  0xE0654970,
  0x775F0DEB,  0x67115FE,  0x3A618661,  0xBAEB7668,  0x842F021A,  0x70055051,  0x819D463D,  0xA140B2D6,
  0xE7C4727A,  0x4DB69328,  0xCD2A8E14,  0x7663819E,  0xD683D41F,  0x69086397,  0x6EEED8A8,  0xA624A15A,
  0x64F2FF5B,  0xE1F01785,  0x31B28944,  0xFFD3980D,  0xE15C9ED3,  0xB00D5AE8,  0x3997DDB4,  0x1EC60321,
  0x314B0D54,  0x7D0ADC57,  0xBBAFBC63,  0x5FA7A9CE,  0x4D2EEB0C,  0x6DBA382C,  0xD77D8767,  0xAD240DA3,
  0xF0EDDFCF,  0xF847BDDA,  0xAFDE3A8A,  0x2D9C6ED0,  0x4A114E6B,  0x4944F5AB,  0xA24ABF56,  0x7894F090,
  0x5281E3CF,  0x88C4BFC9,  0xE587E82E,  0x544C5C5E,  0x66152214,  0xF393152B,  0x22341EEB,  0xF2066B93,
  0xA3F0FD4C,  0x6F0E0CC4,  0xD6F36B6F,  0x10C9851B,  0x781BAAB0,  0xB25D058A,  0x35ECAB38,  0x20EE1BCB,
  0x59DAE04C,  0xF9690756,  0xC529267B,  0x4247C86,  0xBE8F8721,  0x5D5ED46B,  0xB616A25E,  0x11380850,
  0x64ADD8A4,  0xF545EDA8,  0x42CA2CCC,  0xA170BC76,  0x8114F9A7,  0xBF4ABD92,  0xDF0E41A6,  0xF53C6FAA,
  0x40A357E9,  0x61CEF6BC,  0xAA1B08F8,  0xA6D195DD,  0xC2C3C33,  0x68B66BAC,  0x1A293E0F,  0xEEB02112,
  0x2507E7EF,  0x2FAE54EA,  0xEB5E9E9F,  0x3EF3497B,  0xA850511A,  0x68D52AD1,  0x3EA2D62,  0x703D9456,
  0x626C1D83,  0xBE8EBCBD,  0xEA10DF63,  0xA906932C,  0x8792D5DD,  0x2B110575,  0xB062A81D,  0x5A759F65,
  0xC0C4EA0C,  0x937C7319,  0x24B9089A,  0x4638E293,  0xF4B3967F,  0x43CFFDF8,  0xDB90E0C9,  0xB3E8B828,
  0x385AB9DD,  0x6F085277,  0x50004B04,  0x9A30BE0B,  0xDC8F3EAA,  0xA462D488,  0x80DDAC6E,  0xAA19EC01,
  0x992BE4E4,  0xBA912014,  0x67204FE2,  0xABD0E51,  0x6016D9B8,  0xAEFD164B,  0xBABFD1A7,  0xF6026C1C,
  0xA38062B3,  0x203321B8,  0x7418DFEC,  0x9AB6A666,  0x3B3BC0F3,  0x6D206DD0,  0xDC82C803,  0x33DCBCA2,
  0xB56E8971,  0x1E7AA105,  0x9341D295,  0xD6ED1FF8,  0x76830FB0,  0x5B7357F9,  0x3EECA2F4,  0x59BEDAEC,
  0xD45C84C9,  0x6B1E2AD7,  0xFAD6F20A,  0x381CB01A,  0x8A72C453,  0x39BE6E91,  0x5B317322,  0x3EA77570,
  0xA9DCFEEC,  0x3D7B6EA9,  0x693858BE,  0x9C407C39,  0x8A3E7375,  0x7141327C,  0xDB048B6D,  0xC6A1EAAE,
  0xEFB8D229,  0xC323FC90,  0x2CD680C6,  0xF5F25F1E,  0x64E5ED79,  0xA689D9C7,  0x9DC1923B,  0xFD1BBC8C,
  0xDB53CAF7,  0x7EFDF9C4,  0xE7D20ADF,  0x6DD6C760,  0x3D891A1C,  0xD219683,  0xD539C29D,  0xB3899B22,
  0x5A08170D,  0x533584A8,  0xC5ED9B50,  0x610F6C71,  0xB0EA1AFF,  0x26875B51,  0xB3D0131,  0xC558F3F9,
  0xD5F2F0DE,  0x14342882,  0xFC9164E5,  0x80931513,  0x3DE4ACCA,  0xCCDF6C57,  0x136B0384,  0x7E37DBA5,
  0x9EE7A20F,  0x12CD7B7A,  0x1F94D6AD,  0xC5E6F806,  0x351F0A38,  0xCFA4E392,  0x8CD6921C,  0x4D6503A6,
  0xC15E67AB,  0xE02E8126,  0xE702B5FF,  0x443FD0C6,  0xFFFC9F65,  0xF96A3E3E,  0x4F77A0FE,  0x8C2335AE,
  0x7F17AE66,  0x42CE2131,  0xE3CBA1C5,  0xE972E0BC,  0x706E6D8F,  0x2ECAE452,  0x9371CFE7,  0xA52CB1FB,
  0xA4ED5FF6,  0xA66EA2FE,  0x4F88113A,  0xD0CFA1FB,  0xF0CA25BC,  0x88819029,  0x5D6C4833,  0x7708CA1E,
  0xC38615DC,  0xC9AE887E,  0xCD615CD8,  0x248721D5,  0xEB32616A,  0xCB5AA3F,  0x46719CAC,  0x6DFFD2D8,
  0xB802175B,  0xFC3601D9,  0x532ED2D5,  0xCDE9DDD7,  0x400F8840,  0x94BBA003,  0xEEFFB965,  0x29A9D7A7,
  0xD6407A64,  0x87FF01D2,  0xE841D424,  0x5F235570,  0x673CD1E3,  0x6AD15C85,  0x168F226C,  0x8CB15459,
  0xAEA8FB59,  0x96A45BEB,  0x6562CA69,  0xDC7A0B0C,  0xFAC795C,  0xE6E15B1C,  0x2D6185E9,  0xBAFB9FA2,
  0x7451BDA3,  0x686F5AF,  0xEAB562E5,  0x22FBC9FC,  0x96F8491A,  0xC1BD7B9F,  0x4893E971,  0xEDB93199,
  0xAE920C0E,  0xB71CE0DF,  0x996442B9,  0xACDEFF74,  0x58D8FBE5,  0x8F47E47A,  0x513351B6,  0x6EBF0571,
  0xC4D46D6B,  0x843B0F17,  0x3F432827,  0xEEDCBBC8,  0xCD86698F,  0x463A5259,  0x5321D176,  0xE2AD75D4,
  0xE8923582,  0x97F3CF7B,  0x4ADA1C15,  0x1262CDB9,  0x288F25E0,  0xA894ABED,  0x7F4D289C,  0x3545AB73,
  0xD7D02505,  0xB578CCBC,  0xEABB8C17,  0xCF8684A7,  0xE84EE1,  0xF6E34D45,  0x8B296CB,  0x243D7D5F,
  0x8B6D905E,  0xF569E651,  0xB9A102DD,  0xD9A8D328,  0x70FC50B6,  0xE02B0028,  0xEEDAB6E1,  0xA2EA191B,
  0xF68B03BE,  0x42A2CE34,  0x3181683B,  0xBB4223FF,  0x27883463,  0xA5517075,  0x997DD34E,  0x8CD0AB69,
  0x81FE160D,  0xBAA03036,  0x2A7D6066,  0x73A590E5,  0xC42423CA,  0x4D49656E,  0x1F85E39C,  0x3C354C4A,
  0x6D63AE,  0xF2B13EEC,  0x7E65C08F,  0x2F58787C,  0x68F05519,  0x12B1A32C,  0xA35652F0,  0xDD0DA5F7,
  0x7BFC82FC,  0xFAF7E0F0,  0x75FA36D8,  0x998B9A3E,  0x2A3927A,  0xCA13AE7D,  0x2FDADEFD,  0x849C013D,
  0xC5A73CDE,  0xA552901,  0x224843E9,  0x9E9FD195,  0x742947BA,  0x58860E5F,  0x1F03B7CD,  0x469078E2,
  0xB8A2A84B,  0x8CD5431C,  0xA64B3697,  0x3F47EE39,  0xE7834BFA,  0x38EA65B7,  0xB5420165,  0xD84A8812,
  0x96729747,  0x618C84EC,  0xF8EE6E26,  0x8B635E1B,  0xAB1BFBDE,  0xD2F03705,  0xFC54F7FC,  0xFDBD2A42,
  0xC9B8A3E5,  0xA973362F,  0x50A6536F,  0x40E9E56,  0x9D1FD911,  0x49D5E35D,  0x7E4A6CC6,  0x37B42E1A,
  0x5170F8F9,  0xCB4664D9,  0x8054FE39,  0xBB04644A,  0x2B25CE56,  0xC1DC1C07,  0xA2C5555A,  0xCE38E243,
  0xCF4113AC,  0x93A12C05,  0xF3D7D89B,  0x3DB6FF61,  0xB976FD36,  0x2D945A83,  0x76F88F49,  0x18F83CE2,
  0xCB5112FB,  0xDC9E4030,  0x99EE9C0A,  0x42C7BEC8,  0x71E4988D,  0x13E375B7,  0xE21D120,  0xD2629AC3,
  0x6D319B7C,  0xD44D84D7,  0xF24AC8D2,  0xCAD5A861,  0xFFC029A4,  0xA19FE620,  0x378210FC,  0x96595F0D,
  0xB4C2C0B8,  0x6EF81216,  0x5DFAEA83,  0xFD771C5D,  0x322B7E10,  0x3B2DA318,  0xA7869CC1,  0x48FD9416,
  0x2B5D27FF,  0xFF8D445C,  0xC58203A8,  0xBE9F6146,  0x9CCCCF88,  0x85310524,  0x52184E18,  0x1DFCF209,
  0x1DF5C846,  0x1A1A4289,  0x339CF930,  0x11AEB574,  0x6145B206,  0x9EC5ED22,  0xEEC8ACAB,  0x96D8D441,
  0x7FCBF57E,  0xF0FF40A0,  0x93DD9DA2,  0xC05E2953,  0xDCC46BA1,  0x6B4276D7,  0x370B909F,  0xBB262FE6,
  0x4AD54B32,  0x7F4D7C1A,  0xE709F1CB,  0xD79882F2,  0xF33FDB25,  0xC602B335,  0xC52AA589,  0x80C0EFB7,
  0x928C0E5D,  0xEA9717CF,  0x83663836,  0xBB88177,  0xE38F15A8,  0x119931C7,  0xE2935AF6,  0xCA3F1AD3,
  0xE9640CB2,  0x6FE7A421,  0x8C791F43,  0x6ED19D46,  0x1D6E5F37,  0xAF5E54BF,  0x5940FFBE,  0x25341CEB,
  0xEE02072,  0x7B3F60F9,  0xC06EA8FD,  0x35B816A,  0x50B06B7B,  0xDD939DA4,  0x1FD52C2F,  0x3E52BA08,
  0x5F9F129D,  0x92B6A235,  0x1EE30A62,  0x3A208DEA,  0x5068429E,  0x5AE94671,  0x2F03BB9,  0x82453373,
  0xFD4947B9,  0xA36A4009,  0x12AF5EE5,  0x80F97F5E,  0xE0D0A236,  0xA466454C,  0x34561734,  0x7815C14A,
  0x81CF00A1,  0xD7487BE9,  0x45CE7E2,  0x665D2119,  0x487013AD,  0x3CD9348E,  0x76F4A2BF,  0x92415E6,
  0x3E73F8C6,  0x14EEAD0E,  0x9079E715,  0x593B9B7A,  0xEC996C98,  0xA026ED77,  0x42F06176,  0x72BF354C,
  0x714221DA,  0x5A9F9577,  0x56A5B41A,  0xED21A1BD,  0x93BEFB55,  0x941193C4,  0x39BA8E11,  0x1671D2B3,
  0x123BE7B8,  0xD4CC9164,  0x4BEC967B,  0x7E2FA69F,  0x9C89C4F6,  0x2B3B4E6C,  0x1F625821,  0x1279143A,
  0x115EA2CA,  0x27F92B8,  0x46E2E6AD,  0x48B4F26A,  0x97CAF062,  0x8F8A76CC,  0xB6DAB168,  0xF31A44A8,
  0xD01F970A,  0xFBFCBD4,  0x8A9804A1,  0xEB4BEB33,  0xCC9E8C7,  0x2E52D874,  0x44133F81,  0xE582CA57,
  0x92C907B6,  0x5FD54BD0,  0x8D8EAC80,  0x8721468F,  0x5C52558F,  0x41282EC3,  0x86BF543B,  0x83C01DD4,
  0x3D502E68,  0x36FF8370,  0x1D11D251,  0xB1A34C9E,  0xFD303CC7,  0xDC4C946A,  0x8EDB0C79,  0x4F76350A,
  0x590C9C46,  0x63D7598D,  0xE6C07E50,  0xFCD42494,  0xEA791A2E,  0xD608EE6,};

int flash_crc_block(int block_id)
{
    const int LOOKUP_TABLE_LAST_BLOCK = LOOKUP_TABLE_SIZE/2048/64;
    const int LOOKUP_TABLE_LAST_BLOCK_LEN = LOOKUP_TABLE_SIZE - LOOKUP_TABLE_LAST_BLOCK*2048*64;
    const int LOOKUP_TABLE_LAST_BLOCK_PAGE = LOOKUP_TABLE_LAST_BLOCK_LEN/2048 + 1;
    uint32_t crc = 0xFFFFFFFFUL;
    int prog_len, start, end;
    // 74091948/2048/64 = 565.2767028808594
    // 
    if(block_id == LOOKUP_TABLE_LAST_BLOCK){
        start = block_id * 64;
        end   = block_id * 64 + LOOKUP_TABLE_LAST_BLOCK_PAGE;
        prog_len = LOOKUP_TABLE_LAST_BLOCK_LEN;
    }else{
        start = block_id * 64;
        end   = block_id * 64 + 64;
        prog_len = 2048 * 64;
    }
	for(int i=start; i<end; i++)
    {
		int max = 2048;
		if(prog_len < max){
			max = prog_len;
		}
		prog_len -= max;
        //
        uint8_t cmdbuf_1[4] = {FLASH_PAGE_DATA_READ, 0, i >> 8, i & 0xff};
        cs_select();
        spi_write_blocking(spi0, cmdbuf_1, sizeof(cmdbuf_1));
        cs_deselect();
        // wait <60us with ECC, <25us without ECC 
        flash_busy_wait(); 
        // read data 03h
        uint8_t tmp[2048];
        uint8_t cmdbuf_2[4] = {FLASH_READ, 0x00, 0x00, 0};
        cs_select();
        spi_write_blocking(spi0, cmdbuf_2, sizeof(cmdbuf_2));
        spi_read_blocking(spi0, 0, tmp, max);
        cs_deselect();

        for(int i = 0; i < max; i ++){
            unsigned char index = (unsigned char)(crc^tmp[i]);
            crc = (crc>>8)^crc_table[index];
        }
        
    }
    crc ^= 0xFFFFFFFFUL;
    if(crc32_checksums[block_id] == crc){
        printf("CRC=%08X -> OK\n", crc);
        return 0;
    }else{
        printf("CRC=%08X -> ERROR\n", crc);
        return -1;
    }
}